package PeterSheep.AccountsManage;

import PeterSheep.Kernel;
import PeterSheep.Utils.PDatabase;
import PeterSheep.Utils.PEncryption;
import PeterSheep.Utils.PMapAddons;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.regex.Pattern;

public class UAccount {
    PDatabase accountDatabase = new PDatabase();
    private Map<Integer, Boolean> registerProcessIDList = new HashMap<>();
    private int[] registerProcessVCodeList = {};
    private boolean valid = false;
    private int latestError = 200;
    // String password;         (!) Account system not needed storage user password!;
    public String username;
    public String prefix;
    public String email;
    public String uuid;

    public UAccount() {
        try {
            Bukkit.getLogger().info(" (PeterSheep) --[#]> Database connect request send(" + Kernel.pluginConfig.getString("uactSettings.databaseHost") + ")!");
            if(!accountDatabase.connectDatabase("accounts"))
                this.latestError = 4011;
        } catch (SQLException | ClassNotFoundException e) { e.printStackTrace(); }

        latestErrorProcess();
    }

    public boolean loginAccount(String email, String password)
            throws SQLException, UnsupportedEncodingException, NoSuchAlgorithmException {
        Bukkit.getLogger().info(" (PeterSheep) --[#]> Collected a request of Login!");
        ResultSet accountData = this.accountDatabase.getConnectionStatement().executeQuery("SELECT * FROM accounts");
        boolean matched = false;
        int selectCount = 0;
        while(accountData.next()) {
            selectCount++;
            Bukkit.getLogger().info(" (PeterSheep) --[?]> Select from database("+selectCount+")");
            if(accountData.getString("email").equalsIgnoreCase(email))
                if(accountData.getString("password").equalsIgnoreCase(PEncryption.passwordEncryption(password))) {
                    Bukkit.getLogger().info(" (PeterSheep) --[√]> Login data got, username: "+accountData.getString("username"));
                    Bukkit.getLogger().info(" (PeterSheep) --[√]> Login data got, email: "+accountData.getString("email"));
                    Bukkit.getLogger().info(" (PeterSheep) --[√]> Login data got, uuid: "+accountData.getString("uuid"));
                    Bukkit.getLogger().info(" (PeterSheep) --[√]> Login data got, prefix: "+accountData.getString("prefix"));
                    this.username = accountData.getString("username");
                    this.email = accountData.getString("email");
                    this.uuid = accountData.getString("uuid");
                    this.prefix = accountData.getString("prefix");
                    this.valid = true;
                    matched = true;
                    break;
                }
        }

        accountData.close();

        return matched;
    }

    public int registerAccount(String email, String username, String password, Player requestPlayer)
            throws SQLException, UnsupportedEncodingException, NoSuchAlgorithmException, MessagingException {
        Bukkit.getLogger().info(" (PeterSheep) --[#]> Collected a request of Register!");
        boolean allFilterPass = true;
        int latestCheckCode = 200;
        // (?) Check account not exist
        ResultSet accountData = this.accountDatabase.getConnectionStatement().executeQuery("SELECT * FROM accounts");
        while(accountData.next()) {
            if(accountData.getString("username").equalsIgnoreCase(username)) {
                latestCheckCode = 4011;
                allFilterPass = false;
                break;
            }

            if(accountData.getString("email").equalsIgnoreCase(email)) {
                latestCheckCode = 4012;
                allFilterPass = false;
                break;
            }
        }

        if(!Pattern.matches("^([A-Za-z0-9_\\-\\.\\u4e00-\\u9fa5])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,8})$", email)) {
            latestCheckCode = 4021;
            allFilterPass = false;
        }

        if(allFilterPass)
            registerRemoteVerification1(username, email, requestPlayer);


        return latestCheckCode;
    }

    public void logOutAccount() {
        Kernel.userAccountsList.delPlayerAccount(Kernel.userAccountsList.getIdFUsername(this.username));
        this.username = null;
        this.prefix = null;
        this.email = null;
        this.uuid = null;
        this.valid = false;
    }

    public boolean isValid() { return this.valid; }

    // @ Private function
    private void registerRemoteVerification1(String regUsername, String regEmail, Player requestPlayer) throws MessagingException {
        String regRequestIP = requestPlayer.getAddress().getHostString();
        final int verificationCode = (int)(Math.random() * 999999);
        final int processCode = ((int)PMapAddons.getMinObjectValue(this.registerProcessIDList)) + 1;
        // (!) Send email
        String[] sendArgsList = {regEmail, "caden2018@163.com", "localhost"};
        Properties properties = System.getProperties(); properties.setProperty("mail.smtp.host", sendArgsList[2]);
        Session session = Session.getDefaultInstance(properties);
        MimeMessage requestCodeEmail = new MimeMessage(session);
        requestCodeEmail.setFrom(new InternetAddress(sendArgsList[1]));
        requestCodeEmail.setSubject("CodeR-Server Register verification code");
        // -------------------------------->  Message <-------------------------------- //
        requestCodeEmail.setText("You have a "+Kernel.pluginConfig.getString("serverName")+" registration application!\n" +
                "Username applying for registration："+regUsername+"\n" +
                "\n" +
                "Verification Code：\n"+verificationCode+"\n" +
                "Process Code：\n"+processCode+"\n" +
                "\n" +
                "-------------------------------------------\n" +
                "This application is made by "+regRequestIP+"(IP) issue\n" +
                "If you did not make this request, it is very likely that your mailbox has been stolen. Please note the changes!\n" +
                "If everything is confirmed, you can enter /uact reg confirm "+processCode+" "+verificationCode+" in the game\n");
        // -------------------------------->  Message <-------------------------------- //
        Transport.send(requestCodeEmail);
        this.registerProcessIDList.put(processCode, false);
        this.registerProcessVCodeList[this.registerProcessVCodeList.length] = verificationCode;
    }

    private int registerRemoteVerification2(String regUsername, String regEmail, String regUuid, String regPassword,
                                            int verificationCode, int processCode)
            throws UnsupportedEncodingException, NoSuchAlgorithmException, SQLException {
        // (?) Verification code check
        if(this.registerProcessIDList.get(processCode))
            return 4011;

        if(!(this.registerProcessVCodeList[processCode - 1] == verificationCode))
            return 5021;

        // (!) Register request accepted
        Bukkit.getLogger().info(" (PeterSheep) --[#]> Register request is accepted, account is registered. ProcessID: "+processCode+"\n");
        regPassword = PEncryption.passwordEncryption(regPassword);
        String regPrefix = "§2Earth§eVillage";
        this.accountDatabase.insert2Table("email, username, password, prefix, uuid",
            String.format("\"%s\" \"%s\" \"%s\" \"%s\"", email, username, regPassword, regUuid));

        return 200;
    }

    // @ Debugger
    private void latestErrorProcess() {
        if(this.latestError == 4011)
            Bukkit.getLogger().info(" (PeterSheep) --[!]> UAccount Fatal error occurred: Database cannot connect! - "+this.latestError);
    }

    // @ Summon UUID function
    public static String summonUUID() {
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }
}
